package com.atguigu.tingshu.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.TrackClientFeign;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserCollectTable;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.user.mapper.UserCollectMapper;
import com.atguigu.tingshu.user.mapper.UserInfoMapper;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
@RequiredArgsConstructor
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Value("${wechat.login.appId}")
    private String appId;

    @Value("${wechat.login.appSecret}")
    private String appSecret;

    private final UserInfoMapper userInfoMapper;
    private final RestTemplate restTemplate;
    private final RSAPrivateKey rsaPrivateKey;
    private final TrackClientFeign trackClientFeign;
    private final UserCollectMapper userCollectMapper;

    /**
     * 微信登录
     *
     * @param code 微信授权码 临时登录凭证 code
     * @return token
     */
    @Override
    public Object wxLogin(String code) {
        // 判断code是否为空 表示未登录
        if (StringUtils.isEmpty(code)) {
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }

        // url初始化 构建微信登录接口地址
        String url = "https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code";
        // appid=APPID
        url += "&appid=" + appId;
        // secret=SECRET
        url += "&secret=" + appSecret;
        // js_code=JSCODE
        url += "&js_code=" + code;


        // 使用Spring的RestTemplate发起 http get 请求到构建好的微信登录接口url
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
        // 获取微信服务器返回的结果:json字符串
        String result = exchange.getBody();
        // 反序列化为json对象
        JSONObject resultJson = JSONObject.parseObject(result, JSONObject.class);
        // 获取openid 用户唯一标识符
        String openid = resultJson.get("openid").toString();


        // 记录设备号: 模拟(实际情况由前端传递-head中) 并加密
        String deviceID = "123456789";
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        deviceID = bCryptPasswordEncoder.encode(deviceID);


        // 查询用户信息：判断用户是否为第一次登录
        UserInfo userInfo = getOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getWxOpenId, openid));
        // 如果数据库中不存在对应openid的用户信息 则初始化一个新用户并保存到数据库
        if (userInfo == null) {
            userInfo = new UserInfo();
            userInfo.setNickname("新用户");
            userInfo.setAvatarUrl("http://www.atguigu.com/images/index_new/logo.png");
            userInfo.setWxOpenId(openid);
            userInfo.setPassword(deviceID);
            save(userInfo);
        } else {
            // 非第一次登录: 更新
            userInfo.setPassword(deviceID);
            updateById(userInfo);
        }

        // 生成jwt令牌：用户id + 当前时间
        Map map = new HashMap();
        map.put("userId", userInfo.getId());
        map.put("time", System.currentTimeMillis());

        // 存储vip状态 0-非会员 1-会员
        map.put("isVip", userInfo.getIsVip());
        // 存储vip到期时间
        map.put("vipExpireTime", userInfo.getVipExpireTime().getTime());

        // 将字符串 和 私钥 生成令牌
        Jwt encode = JwtHelper.encode(JSONObject.toJSONString(map), new RsaSigner(rsaPrivateKey));

        String token = encode.getEncoded();
        map.put("token", token);
        // 返回调用方 用于后续的身份验证和授权
        return map;
    }

    /**
     * 刷新新令牌
     *
     * @param deviceID 设备号
     * @return 新令牌
     */
    @Override
    public Map getNewToken(String deviceID) {
        // 获取request
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();

        // 获取旧令牌: http请求头中获取token的值
        String token = request.getHeader("token");

        // 令牌为空：表示用户未提供身份认证信息 抛异常未登录
        if (StringUtils.isEmpty(token)) {
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }

        try {
            // 校验：使用公钥对token进行解码和验证 确保token的合法性
            Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(SystemConstant.PUBLIC_KEY));
            // 解析载荷：从解码后的token中获取载荷部分的信息
            String claims = jwt.getClaims();
            // 反序列化：将json字符串反序列化为json对象
            JSONObject jsonObject = JSONObject.parseObject(claims, JSONObject.class);

            // 获取用户id
            String userId = jsonObject.get("userId").toString();
            // 获取时间
            Long loginTime = jsonObject.getLong("time");
            // 数据库查询用户的最后一次的设备号
            UserInfo userInfo = getById(Long.valueOf(userId));

            // 旧token合法 校验设备
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            // 验证用户输入的设备号是否与数据库中存储的密码匹配
            if (bCryptPasswordEncoder.matches(deviceID, userInfo.getPassword())) {
                // TODO-判断是否需要强制失效(条件: 令牌已经刷新过!!!!!!!!!!)
                if (System.currentTimeMillis() - loginTime > 60000) {
                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
                }
                // 存储新的token
                Map map = new HashMap();
                map.put("userId", userInfo.getId());
                map.put("time", System.currentTimeMillis());
                // 使用私钥对map中的信息加密 生成新的token
                Jwt encode = JwtHelper.encode(JSONObject.toJSONString(map), new RsaSigner(rsaPrivateKey));
                token = encode.getEncoded();
                // 返回结果包装
                map = new HashMap();
                map.put("token", token);
                // 返回
                return map;
            }
        } catch (Exception e) {
            // 有旧令牌但是校验不通过
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @Override
    public UserInfoVo getUserInfo() {
        // 获取用户id
        Long userId = AuthContextHolder.getUserId();
        // 根据用户id查询用户信息
        UserInfo userInfo = getById(userId);
        // 封装返回VO
        UserInfoVo userInfoVo = new UserInfoVo();
        // 复制属性
        BeanUtils.copyProperties(userInfo, userInfoVo);

        return userInfoVo;
    }

    /**
     * 用户收藏
     *
     * @param trackId 声音id
     */
    @Override
    public void collect(Long trackId) {
        //  判断声音是否存在
        if (trackClientFeign.getTrackInfo(trackId)) {
            // 获取用户id
            Long userId = AuthContextHolder.getUserId();
            // 删旧的
            userCollectMapper.delete(
                    new LambdaQueryWrapper<UserCollectTable>()
                            .eq(UserCollectTable::getUserId, userId)
                            .eq(UserCollectTable::getTrackId, trackId));
            // 记录用户id和声音id
            UserCollectTable userCollect = new UserCollectTable();
            userCollect.setUserId(userId);
            userCollect.setTrackId(trackId);
            userCollectMapper.insert(userCollect);
        }
    }

    /**
     * 用户取消收藏
     *
     * @param trackId 声音id
     */
    @Override
    public void cancelCollect(Long trackId) {
        // 获取用户id
        Long userId = AuthContextHolder.getUserId();
        // 删
        userCollectMapper.delete(
                new LambdaQueryWrapper<UserCollectTable>()
                        .eq(UserCollectTable::getUserId, userId)
                        .eq(UserCollectTable::getTrackId, trackId));
    }

    /**
     * 查询用户是否收藏了声音
     *
     * @param trackId 声音id
     * @return 结果
     */
    @Override
    public Object isCollect(Long trackId) {
        return userCollectMapper.selectOne(
                new LambdaQueryWrapper<UserCollectTable>()
                        .eq(UserCollectTable::getTrackId, trackId)
                        .eq(UserCollectTable::getUserId, AuthContextHolder.getUserId()))
                != null;
    }
}
